/**
 * 将字符串解析为日期对象
 *
 * @param {String}
 *            日期字符串
 * @param {String}
 *            日期格式
 * @returns
 * @throws
 */
var parseDate=function() {
    var timeStr, format;
    if (arguments.length === 0) {
        return new Date();
    } else if (arguments.length === 1) {
        timeStr = arguments[0];
        format = 'yyyy-MM-dd HH:mm:ss';
    } else if (arguments.length === 2) {
        timeStr = arguments[0];
        format = arguments[1];
    }

    if (timeStr.length != format.length) {
        alert('参数错误');
        return;
    }
    var yearExpr = /(yyyy)+|(YYYY)+|(yy)+|(YY)+/g;
    var monthExpr = /(MM)+|(M)+/g;
    var dateExpr = /(dd)+|(DD)+|(d)+|(D)+/g;
    var hourExpr = /(HH)+|(H)+/g;
    var miniteExpr = /(mm)+|(m)+/g;
    var secondExpr = /(ss)+|(SS)|(s)+|(S)+/g;

    var getStr = function (expr) {

        var arr = expr.exec(format);
        if (!arr || arr.length === 0){
            return;
        }
        var p = arr[0];
        var start = format.indexOf(p);
        return timeStr.substr(start, p.length);
    };

    var ret = new Date();
    var year = getStr(yearExpr);
    if (year) {
        ret.setYear(parseInt(year));
    } else {
        ret.setYear(1971);
    }
    var month = getStr(monthExpr);
    if (month) {
        ret.setMonth(parseInt(month) - 1);
    } else {
        ret.setMonth(0);
    }
    var date = getStr(dateExpr);
    if (date) {
        ret.setDate(parseInt(date));
    } else {
        ret.setDate(1);
    }
    var hour = getStr(hourExpr);
    if (hour) {
        ret.setHours(parseInt(hour));
    } else {
        ret.setHours(0);
    }
    var minite = getStr(miniteExpr);
    if (minite) {
        ret.setMinutes(parseInt(minite));
    } else {
        ret.setMinutes(0);
    }
    var second = getStr(secondExpr);
    if (second) {
        ret.setSeconds(parseInt(second));
    } else {
        ret.setSeconds(0);
    }

    return ret;
};
/**
 * 将日期格式化为字符串
 *
 * @param {Date}
 *            日期
 * @param {String}
 *            日期格式
 * @returns
 * @throws
 */
var formatDate=function() {
    var time, format;
    if (arguments.length === 0) {
        time = new Date();
        format = 'yyyy-MM-dd HH:mm:ss';
    } else if (arguments.length === 1) {
        time = arguments[0];
        format = 'yyyy-MM-dd HH:mm:ss';
    } else if (arguments.length === 2) {
        time = arguments[0];
        format = arguments[1];
    }
    var dateStr = format;
    var yearExpr = /(yyyy)+|(YYYY)+|(yy)+|(YY)+/g;
    var monthExpr = /(MM)+|(M)+/g;
    var dateExpr = /(dd)+|(DD)+|(d)+|(D)+/g;
    var hourExpr = /(HH)+|(H)+/g;
    var miniteExpr = /(mm)+|(m)+/g;
    var secondExpr = /(ss)+|(SS)|(s)+|(S)+/g;

    var repStr = function (expr) {
        var arr = expr.exec(format);
        if (!arr || arr.length === 0){
            return;
        }
        var p = arr[0];
        var twobit = function (s) {
            s = s + '';
            return s.length < 2 ? "0" + s : s;
        };
        if (p && (p == 'yyyy' || p == 'YYYY')) {
            dateStr = dateStr.replace(p, time.getFullYear());
        } else if (p && (p == 'yy' || p == 'YY')) {
            var yearStr = time.getYear() + '';
            dateStr = dateStr.replace(p, yearStr.substr(2));
        } else if (p && p == 'MM') {
            dateStr = dateStr.replace(p, twobit(time.getMonth() + 1));
        } else if (p && p == 'M') {
            dateStr = dateStr.replace(p, time.getMonth() + 1);
        } else if (p && (p == 'DD' || p == 'dd')) {
            dateStr = dateStr.replace(p, twobit(time.getDate()));
        } else if (p && (p == 'D' || p == 'd')) {
            dateStr = dateStr.replace(p, time.getDate());
        } else if (p && p == 'HH') {
            dateStr = dateStr.replace(p, twobit(time.getHours()));
        } else if (p && p == 'H') {
            dateStr = dateStr.replace(p, time.getHours());
        } else if (p && p == 'mm') {
            dateStr = dateStr.replace(p, twobit(time.getMinutes()));
        } else if (p && p == 'm') {
            dateStr = dateStr.replace(p, time.getMinutes());
        } else if (p && (p == 'SS' || p == 'ss')) {
            dateStr = dateStr.replace(p, twobit(time.getSeconds()));
        } else if (p && (p == 'S' || p == 's')) {
            dateStr = dateStr.replace(p, time.getSeconds());
        } else {
            return null;
        }
    };

    repStr(yearExpr);
    repStr(monthExpr);
    repStr(dateExpr);
    repStr(hourExpr);
    repStr(miniteExpr);
    repStr(secondExpr);

    return dateStr;
};

/**
 * 回到当前时刻
 * @returns {Date}
 */
Date.prototype.toNow = function() {
    this.setTime(Date.now());
    return this;
};
/**
 * 回到午夜零点
 * @returns {Date}
 */
Date.prototype.toMidnight = function() {
    this.setHours(0);
    this.setMinutes(0);
    this.setSeconds(0);
    return this;
};
Date.prototype.toEndNight = function() {
    this.setHours(23);
    this.setMinutes(59);
    this.setSeconds(59);
    return this;
};
/**
 * 获取若干秒前
 * @param secs
 * @returns {Date}
 */
Date.prototype.secsAgo = function(secs) {
    secs = secs ? secs - 0 : 0;
    return new Date(this.getTime() - secs * 1000);
};
/**
 * 获取几天前
 * @param days
 * @returns {Date}
 */
Date.prototype.daysAgo = function(days) {
    days = days ? days - 0 : 1;
    return this.secsAgo(days * 86400).toMidnight();
};

/**
 * 获取月初第一天
 * @param offset
 * @returns {Date}
 */
Date.prototype.monthBegin = function(offset) {
    offset = offset ? offset - 0 : 0;
    var days = this.getDate() - 1 - offset;
    return this.daysAgo(days);
};

/**
 * 今年第多少天
 * @returns {number}
 */
Date.prototype.getDaysOfYear = function() {
    var first_day = new Date(this.getFullYear(), 0, 1);
    var micro_secs = this.getTime() - first_day.getTime();
    return parseInt(micro_secs / 8.64E7) + 1;
};

/**
 * 今年第多少周
 * @param start
 * @returns {number}
 */
Date.prototype.getWeeksOfYear = function(start) {
    var offset = this.getDaysOfYear() - 1;
    var remain = offset % 7;
    if (remain > 0) {
        var first_day = new Date(this.getFullYear(), 0, 1);
        if (start) { //周一作为一周开始
            offset += (first_day.getDay() + 6) % 7;
        } else { //周日作为一周开始
            offset += first_day.getDay();
        }
    }
    return parseInt(offset / 7);
};
/**
 * 返回下个月的第一天的Date对象
 * @returns {Date}
 */
Date.prototype.getStartOfNextMonth = function(){
    var newDate = new Date(this);
    newDate.setDate(15); //确保月数不会进位
    newDate.setMonth(this.getMonth()+1);
    newDate.setDate(1);
    newDate.toMidnight();
    return newDate;
};

/**
 * 返回下个月的最后一天的Date对象
 * @returns {Date}
 */
Date.prototype.getEndOfNextMonth = function(){
    var newDate = new Date(this);
    newDate.setDate(15); //确保月数不会进位
    newDate.setMonth(this.getMonth() + 2); //加两个月
    newDate.setDate(0); //再退回上个月的最后一天
    newDate.toEndNight();
    return newDate;
};
/**
 * 返回一个加上若干个月的新Date
 * 注1：Date(2-28).plusMonth(1)=Date(3-28)。如果需要变成3-31，需要另外的函数来处理。
 * 注2：Date(1-31).plusMonth(1)=Date(2-28)或Date(2-29)
 * @param num
 * @returns {Date}
 */
Date.prototype.plusMonths = function(num) {
    var newDate = new Date(this);
    newDate.setMonth(this.getMonth() + num); //setMonth()会自动除以12
    //注意：此时，月数可能会自动进位，比如：1-31加上num=1的情况，会变成3-3（非闰年）或3-2（闰年），即2-31自动转换为下个月的某一天。

    var currentMonth = this.getMonth() + this.getFullYear() * 12; //获得月的绝对值
    var diff = (newDate.getMonth() + newDate.getFullYear() * 12) - currentMonth; //计算新旧两个月绝对值的差

    if (diff != num) { //如果月绝对值的差和加上的月数不一样，说明月进位了，此时需要退一个月
        //setDate(0)表示变成上个月的最后一天
        newDate.setDate(0);
    }

    return newDate;
};
/**
 * 返回一个减去days天的新Date
 * @param days
 * @returns {Date}
 */
Date.prototype.minusDays = function(days) {
    return new Date(this.getTime() - days*60*60*24*1000);
};
/**
 * 返回一个加上days天的新Date
 * @param days
 * @returns {Date}
 */
Date.prototype.plusDays = function(days) {
    return new Date(this.getTime() + days*60*60*24*1000);
};